Skip to content

feat: Api response extensions#754

Open
benjaminVadon wants to merge 4 commits intomainfrom
api-response-extensions
Open

feat: Api response extensions#754
benjaminVadon wants to merge 4 commits intomainfrom
api-response-extensions

Conversation

@benjaminVadon
Copy link
Copy Markdown
Contributor

No description provided.

…re data retrieving

Add onXXX method to apply code when success or error happens with strong typing (not null)
Add mapXXX method to transform response content
@benjaminVadon benjaminVadon requested a review from a team April 22, 2026 13:16
@benjaminVadon benjaminVadon self-assigned this Apr 22, 2026
Copilot AI review requested due to automatic review settings April 22, 2026 13:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Kotlin extension helpers to streamline handling of ApiResponse success/error cases (callbacks + mapping), and extends the core ApiResponse model with an isError() convenience method.

Changes:

  • Introduces ApiResponse extensions: onSuccess, onError, on, mapSuccess, mapError, map with Sentry reporting on invalid states.
  • Adds ApiResponseException / ApiErrorException used for reporting inconsistent API payloads.
  • Adds isError() to ApiResponse model.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseExt.kt New ApiResponse extension API for success/error callbacks and mapping with Sentry capture.
Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiResponseException.kt New exception for “success but data is null” cases.
Network/src/main/kotlin/com/infomaniak/core/network/utils/apiResponse/ApiErrorException.kt New exception for “error but error payload is null” cases.
Network/Models/src/main/java/com/infomaniak/core/network/models/ApiResponse.kt Adds isError() helper to support new extensions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +24 to +27
inline fun <T> ApiResponse<T>.onSuccess(block: T.() -> Unit): ApiResponse<T> = apply {
if (isSuccess()) {
data?.run(block) ?: Sentry.captureException(ApiResponseException(this))
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onSuccess assumes a non-null data payload when result == SUCCESS, but the generic type parameter T is unconstrained (it can be nullable, e.g. ApiResponse<Foo?>). In that case a null success payload would be treated as an error and reported to Sentry. Consider constraining the extension to T : Any (and same for mapSuccess) or explicitly documenting/handling the nullable-success case without logging an exception.

Copilot uses AI. Check for mistakes.
inline fun <T, R> ApiResponse<T>.map(
onSuccess: T.() -> R,
onError: ApiError.() -> R
): R? = mapSuccess(onSuccess) ?: mapError(onError)
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map() uses mapSuccess(...) ?: mapError(...), which makes null an ambiguous value: a successful mapping that legitimately returns null will still fall through to the error branch check. Using an explicit when on isSuccess()/isError() (or result) avoids the ambiguity and makes it clearer which branch is executed.

Suggested change
): R? = mapSuccess(onSuccess) ?: mapError(onError)
): R? = when {
isSuccess() -> mapSuccess(onSuccess)
isError() -> mapError(onError)
else -> null
}

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

Failed to generate code suggestions for PR

@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 Security concerns

Sensitive information exposure:
The new toString() override in ApiResponse prints data, uri, and error fields. The new ApiResponseException and ApiErrorException classes include the full response string in their messages, and the onSuccess/onError extensions capture these exceptions with Sentry. If API responses contain sensitive data (e.g., tokens, user details), this information may be transmitted to Sentry and appear in logs. Consider sanitizing exception details or avoiding printing sensitive fields in toString().

⚡ Recommended focus areas for review

False-positive Sentry reports

The onSuccess extension reports an ApiResponseException to Sentry whenever data is null on a successful response, but ApiResponse declares data as T?. If an endpoint uses a nullable type (e.g., String?) or returns no data (e.g., Unit), a legitimate success response with null data will incorrectly trigger a Sentry report.

inline fun <T> ApiResponse<T>.onSuccess(block: T.() -> Unit): ApiResponse<T> = apply {
    if (isSuccess()) {
        data?.run(block) ?: Sentry.captureException(ApiResponseException(this))
    }
}
Sensitive data exposure

The new toString() override includes data, uri, and error fields. The new exception classes embed the full response in their messages, and the onSuccess/onError extensions send these exceptions to Sentry. If an API response contains sensitive information such as authentication tokens or personal data, it may be transmitted to Sentry. The actual risk depends on the contents of production API responses.

override fun toString(): String {
    return "ApiResponse(result=$result, data=$data, uri=$uri, error=$error, responseAt=$responseAt )"
}

@github-actions
Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants